package com.javacv.plus.tools.converter;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.opencv.opencv_core.Mat;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;

import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*;

/**
 * 图像格式转换器工具
 * 支持多种图像格式之间的转换和批量处理
 */
@Slf4j
public class ImageFormatConverter {
    
    // 支持的图像格式
    public static final Set<String> SUPPORTED_FORMATS = new HashSet<>(Arrays.asList(
        "jpg", "jpeg", "png", "bmp", "tiff", "tif", "webp", "gif"
    ));
    
    // OpenCV支持的格式
    public static final Set<String> OPENCV_FORMATS = new HashSet<>(Arrays.asList(
        "jpg", "jpeg", "png", "bmp", "tiff", "tif", "webp"
    ));
    
    /**
     * 转换单个图像文件
     */
    public ConversionResult convertSingle(String inputPath, String outputPath, 
                                        String targetFormat, ConversionOptions options) {
        long startTime = System.currentTimeMillis();
        
        try {
            validateInputs(inputPath, targetFormat);
            
            String inputFormat = getFileExtension(inputPath).toLowerCase();
            targetFormat = targetFormat.toLowerCase();
            
            log.info("开始转换: {} -> {} ({})", inputPath, outputPath, targetFormat);
            
            if (OPENCV_FORMATS.contains(inputFormat) && OPENCV_FORMATS.contains(targetFormat)) {
                return convertWithOpenCV(inputPath, outputPath, targetFormat, options, startTime);
            } else {
                return convertWithJava(inputPath, outputPath, targetFormat, options, startTime);
            }
            
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("图像转换失败: {} -> {}", inputPath, outputPath, e);
            return new ConversionResult(inputPath, outputPath, targetFormat, 
                                      false, duration, e.getMessage(), 0, 0);
        }
    }
    
    /**
     * 批量转换图像文件
     */
    public BatchConversionResult convertBatch(String sourceDir, String targetDir, 
                                            String targetFormat, ConversionOptions options) {
        log.info("开始批量转换: {} -> {} (格式: {})", sourceDir, targetDir, targetFormat);
        
        List<ConversionResult> results = new ArrayList<>();
        long batchStartTime = System.currentTimeMillis();
        
        try {
            Path sourcePath = Paths.get(sourceDir);
            Path targetPath = Paths.get(targetDir);
            
            // 创建目标目录
            Files.createDirectories(targetPath);
            
            // 查找所有图像文件
            try (Stream<Path> files = Files.walk(sourcePath)) {
                files.filter(Files::isRegularFile)
                     .filter(this::isImageFile)
                     .forEach(file -> {
                         String inputPath = file.toString();
                         String relativePath = sourcePath.relativize(file).toString();
                         String outputPath = targetPath.resolve(
                             changeFileExtension(relativePath, targetFormat)
                         ).toString();
                         
                         // 创建输出目录
                         try {
                             Files.createDirectories(Paths.get(outputPath).getParent());
                         } catch (IOException e) {
                             log.warn("创建输出目录失败: {}", outputPath, e);
                         }
                         
                         ConversionResult result = convertSingle(inputPath, outputPath, 
                                                               targetFormat, options);
                         results.add(result);
                     });
            }
            
        } catch (Exception e) {
            log.error("批量转换失败", e);
        }
        
        long batchDuration = System.currentTimeMillis() - batchStartTime;
        
        BatchConversionResult batchResult = new BatchConversionResult(
            sourceDir, targetDir, targetFormat, results, batchDuration
        );
        
        log.info("批量转换完成: 总文件: {}, 成功: {}, 失败: {}, 耗时: {}ms", 
                batchResult.getTotalFiles(), batchResult.getSuccessCount(), 
                batchResult.getFailureCount(), batchDuration);
        
        return batchResult;
    }
    
    /**
     * 使用OpenCV进行转换
     */
    private ConversionResult convertWithOpenCV(String inputPath, String outputPath, 
                                             String targetFormat, ConversionOptions options,
                                             long startTime) {
        Mat image = imread(inputPath, IMREAD_COLOR);
        
        if (image.empty()) {
            throw new RuntimeException("无法加载图像: " + inputPath);
        }
        
        try {
            // 应用转换选项
            if (options != null) {
                applyConversionOptions(image, options);
            }
            
            // 设置编码参数
            int[] params = getEncodingParams(targetFormat, options);
            
            boolean success = imwrite(outputPath, image, params);
            
            if (!success) {
                throw new RuntimeException("保存图像失败: " + outputPath);
            }
            
            long duration = System.currentTimeMillis() - startTime;
            File outputFile = new File(outputPath);
            
            log.debug("OpenCV转换成功: {} -> {} ({}ms)", inputPath, outputPath, duration);
            
            return new ConversionResult(inputPath, outputPath, targetFormat, 
                                      true, duration, null, 
                                      image.cols(), image.rows());
        } finally {
            image.release();
        }
    }
    
    /**
     * 使用Java标准库进行转换
     */
    private ConversionResult convertWithJava(String inputPath, String outputPath, 
                                           String targetFormat, ConversionOptions options,
                                           long startTime) throws IOException {
        BufferedImage image = ImageIO.read(new File(inputPath));
        
        if (image == null) {
            throw new RuntimeException("无法加载图像: " + inputPath);
        }
        
        // 应用转换选项 (简化版)
        if (options != null && options.getQuality() != null && options.getQuality() < 100) {
            // 对于Java标准库，质量调整比较复杂，这里简化处理
            log.warn("Java标准库转换不支持质量调整，使用默认质量");
        }
        
        boolean success = ImageIO.write(image, targetFormat, new File(outputPath));
        
        if (!success) {
            throw new RuntimeException("保存图像失败: " + outputPath);
        }
        
        long duration = System.currentTimeMillis() - startTime;
        
        log.debug("Java转换成功: {} -> {} ({}ms)", inputPath, outputPath, duration);
        
        return new ConversionResult(inputPath, outputPath, targetFormat, 
                                  true, duration, null, 
                                  image.getWidth(), image.getHeight());
    }
    
    /**
     * 应用转换选项
     */
    private void applyConversionOptions(Mat image, ConversionOptions options) {
        if (options.getResize() != null) {
            ResizeOptions resize = options.getResize();
            org.bytedeco.opencv.opencv_core.Size newSize = 
                new org.bytedeco.opencv.opencv_core.Size(resize.getWidth(), resize.getHeight());
            resize(image, image, newSize);
        }
        
        if (options.getGrayscale() != null && options.getGrayscale()) {
            Mat gray = new Mat();
            cvtColor(image, gray, COLOR_BGR2GRAY);
            cvtColor(gray, image, COLOR_GRAY2BGR);
            gray.release();
        }
    }
    
    /**
     * 获取编码参数
     */
    private int[] getEncodingParams(String format, ConversionOptions options) {
        List<Integer> params = new ArrayList<>();
        
        switch (format.toLowerCase()) {
            case "jpg":
            case "jpeg":
                params.add(IMWRITE_JPEG_QUALITY);
                params.add(options != null && options.getQuality() != null ? 
                          options.getQuality() : 90);
                break;
            case "png":
                params.add(IMWRITE_PNG_COMPRESSION);
                params.add(options != null && options.getCompression() != null ? 
                          options.getCompression() : 3);
                break;
            case "webp":
                params.add(IMWRITE_WEBP_QUALITY);
                params.add(options != null && options.getQuality() != null ? 
                          options.getQuality() : 90);
                break;
        }
        
        return params.stream().mapToInt(Integer::intValue).toArray();
    }
    
    /**
     * 验证输入参数
     */
    private void validateInputs(String inputPath, String targetFormat) {
        if (!Files.exists(Paths.get(inputPath))) {
            throw new IllegalArgumentException("输入文件不存在: " + inputPath);
        }
        
        if (!SUPPORTED_FORMATS.contains(targetFormat.toLowerCase())) {
            throw new IllegalArgumentException("不支持的目标格式: " + targetFormat);
        }
    }
    
    /**
     * 检查是否为图像文件
     */
    private boolean isImageFile(Path file) {
        String extension = getFileExtension(file.toString()).toLowerCase();
        return SUPPORTED_FORMATS.contains(extension);
    }
    
    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        int lastDot = filename.lastIndexOf('.');
        return lastDot == -1 ? "" : filename.substring(lastDot + 1);
    }
    
    /**
     * 更改文件扩展名
     */
    private String changeFileExtension(String filename, String newExtension) {
        int lastDot = filename.lastIndexOf('.');
        String nameWithoutExt = lastDot == -1 ? filename : filename.substring(0, lastDot);
        return nameWithoutExt + "." + newExtension;
    }
    
    /**
     * 转换选项
     */
    public static class ConversionOptions {
        private Integer quality;
        private Integer compression;
        private Boolean grayscale;
        private ResizeOptions resize;
        
        public static ConversionOptions defaults() {
            return new ConversionOptions();
        }
        
        public ConversionOptions quality(int quality) {
            this.quality = Math.max(1, Math.min(100, quality));
            return this;
        }
        
        public ConversionOptions compression(int compression) {
            this.compression = Math.max(0, Math.min(9, compression));
            return this;
        }
        
        public ConversionOptions grayscale(boolean grayscale) {
            this.grayscale = grayscale;
            return this;
        }
        
        public ConversionOptions resize(int width, int height) {
            this.resize = new ResizeOptions(width, height);
            return this;
        }
        
        // Getters
        public Integer getQuality() { return quality; }
        public Integer getCompression() { return compression; }
        public Boolean getGrayscale() { return grayscale; }
        public ResizeOptions getResize() { return resize; }
    }
    
    /**
     * 调整大小选项
     */
    public static class ResizeOptions {
        private final int width;
        private final int height;
        
        public ResizeOptions(int width, int height) {
            this.width = width;
            this.height = height;
        }
        
        public int getWidth() { return width; }
        public int getHeight() { return height; }
    }
    
    /**
     * 转换结果
     */
    public static class ConversionResult {
        private final String inputPath;
        private final String outputPath;
        private final String targetFormat;
        private final boolean success;
        private final long duration;
        private final String errorMessage;
        private final int width;
        private final int height;
        
        public ConversionResult(String inputPath, String outputPath, String targetFormat,
                              boolean success, long duration, String errorMessage,
                              int width, int height) {
            this.inputPath = inputPath;
            this.outputPath = outputPath;
            this.targetFormat = targetFormat;
            this.success = success;
            this.duration = duration;
            this.errorMessage = errorMessage;
            this.width = width;
            this.height = height;
        }
        
        // Getters
        public String getInputPath() { return inputPath; }
        public String getOutputPath() { return outputPath; }
        public String getTargetFormat() { return targetFormat; }
        public boolean isSuccess() { return success; }
        public long getDuration() { return duration; }
        public String getErrorMessage() { return errorMessage; }
        public int getWidth() { return width; }
        public int getHeight() { return height; }
    }
    
    /**
     * 批量转换结果
     */
    public static class BatchConversionResult {
        private final String sourceDir;
        private final String targetDir;
        private final String targetFormat;
        private final List<ConversionResult> results;
        private final long totalDuration;
        
        public BatchConversionResult(String sourceDir, String targetDir, String targetFormat,
                                   List<ConversionResult> results, long totalDuration) {
            this.sourceDir = sourceDir;
            this.targetDir = targetDir;
            this.targetFormat = targetFormat;
            this.results = new ArrayList<>(results);
            this.totalDuration = totalDuration;
        }
        
        public int getTotalFiles() { return results.size(); }
        public long getSuccessCount() { return results.stream().mapToLong(r -> r.isSuccess() ? 1 : 0).sum(); }
        public long getFailureCount() { return results.stream().mapToLong(r -> r.isSuccess() ? 0 : 1).sum(); }
        public double getSuccessRate() { 
            return results.isEmpty() ? 0.0 : (double) getSuccessCount() / getTotalFiles(); 
        }
        
        // Getters
        public String getSourceDir() { return sourceDir; }
        public String getTargetDir() { return targetDir; }
        public String getTargetFormat() { return targetFormat; }
        public List<ConversionResult> getResults() { return new ArrayList<>(results); }
        public long getTotalDuration() { return totalDuration; }
    }
} 